package com.zzhao.calulator;

import java.math.BigDecimal;
import java.util.Stack;

public class Calulator {
	private static final int ROUND=6;

	private BigDecimal Operarte(BigDecimal B, char op, BigDecimal A) {
		BigDecimal c = null;
		switch (op) {
		case '+':
			c = A.add(B);
			break;
		case '-':
			c = B.subtract(A);
			break;
		case '*':
			c = B.multiply(A);
			break;
		case '/':
			if(A.doubleValue()==0)
				A=new BigDecimal("1");
			c =B.divide(A,ROUND, BigDecimal.ROUND_HALF_EVEN);
			break;
		}
		return c;
	}

	private boolean IsOperator(char c) {
		if (c == '+' || c == '-' || c == '*' || c == '/' || c == '('
				|| c == ')' || c == '#')
			return true;
		else
			return false;

	}

	private char Precede(char a, char b) {
		char c = '=';
		switch (a) {
		case '+':
			if (b == '+' || b == '-' || b == ')' || b == '#') {
				c = '>';
				return c;
			} else if (b == '*' || b == '/' || b == '(') {
				c = '<';
				return c;
			}

			break;
		case '-':
			if (b == '+' || b == '-' || b == ')' || b == '#') {
				c = '>';
				return c;
			} else if (b == '*' || b == '/' || b == '(') {
				c = '<';
				return c;
			}

			break;
		case '*':
			if (b == '+' || b == '*' || b == '-' || b == '/' || b == ')'
					|| b == '#') {
				c = '>';
				return c;
			} else if (b == '(') {
				c = '<';
				return c;
			}
			break;
		case '/':
			if (b == '+' || b == '/' || b == '-' || b == '*' || b == ')'
					|| b == '#') {
				c = '>';
				return c;
			} else if (b == '(') {
				c = '<';
				return c;
			}
			break;

		case '(':
			if (b == '+' || b == '-' || b == '*' || b == '/') {
				c = '<';
				return c;
			} else if (b == ')') {
				c = '=';
				return c;
			}
			break;
		case '#':
			if (b == '+' || b == '-' || b == '*' || b == '/' || b == '('
					|| b == ')') {
				c = '<';
				return c;
			} else if (b == '#')
				return '=';
			break;
		}
		return c;
	}

	private double atof(char c) {
		return c - '0';
	}

	public double EvaluateExpression(String which) {
		char[] s = which.toCharArray();
		Stack<Character> optr = new Stack<Character>();
		optr.push('#');
		Stack<BigDecimal> opnd = new Stack<BigDecimal>();
		char c = s[0], op='+';
		int i = 0;
		BigDecimal a=new BigDecimal("0"), b=new BigDecimal("0");
		while (c != '#' || optr.peek().toString().charAt(0) != '#') {
			if (!IsOperator(c)) {
				BigDecimal Temp=new BigDecimal("0");
				BigDecimal C=new BigDecimal("0");
				BigDecimal D=new BigDecimal("0");
				if((i-1)>=0&&!IsOperator(s[i-1])&&c!='.'){
					Temp=opnd.pop();
					C=new BigDecimal(atof(c));
					D=new BigDecimal(10+"");
					Temp=Operarte(C, '+', Operarte(Temp, '*', D));
					opnd.push(Temp);
				}else if(c=='.'){
					Temp=opnd.pop();
					i++;int n=1;
					while(!IsOperator(s[i])&&(s[i]!='#'||optr.peek().toString().charAt(0) != '#')){
						C=new BigDecimal(s[i++]-'0');
						D=new BigDecimal(Math.pow(0.1,n ));
						Temp=Operarte(Operarte(C, '*', D), '+', Temp);
						n++;
					}
					i--;
					opnd.push(Temp);
				}else{
					Temp=new BigDecimal(atof(c));
					opnd.push(Temp);
				} 
				c = s[++i];

			} else
				switch (Precede(optr.peek().toString().charAt(0), c)) {
				case '<':
					optr.push(c);
					c = s[++i];
					break;
				case '=':
					if(!optr.isEmpty())
						optr.pop();
					c = s[++i];
					break;
				case '>':
					if(!optr.isEmpty())
						op = optr.pop().toString().charAt(0);
					if(!opnd.isEmpty())
						a =opnd.pop();
					if(!opnd.isEmpty())
						b =opnd.pop();
					opnd.push(Operarte(b, op, a));
					break;
				}
		}

		return opnd.peek().setScale(ROUND, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

}
